import json
import multiprocessing
import threading
import time

from condition import Condition
from guards import AllGuard, AnyGuard, NotGuard
from log import fsm_logger as logger
from state import State
from transition import Transition


class FiniteStateMachine:
    def __init__(self, name=None, states=None, initial_state=None):
        self.name = name
        if states is not None:
            # 必须为State对象列表
            assert all(isinstance(state, State) for state in states), \
                'states必须为State对象列表'
            # 不允许重名
            assert len(set(state.name for state in states)) == len(states), \
                'states中存在重名状态'
        else:
            self.states = []

        self.initial_state = initial_state
        self._current_state = initial_state
        self.global_parameters = {}

        # 开启线程
        self.t = None

    @property
    def current_state(self):
        return self._current_state

    def activate(self):
        self.set_parameter('_active', True)

    def deactivate(self):
        self.set_parameter('_active', False)

    @current_state.setter
    def current_state(self, value):
        if value is None:
            self._current_state = None
            return
        if not isinstance(value, State):
            raise ValueError('current_state必须为State对象')
        _has = False
        for state in self.states:
            if state.name == value.name:
                self._current_state = state
                self._current_state.is_active = True
                _has = True
            else:
                state.is_active = False
        assert _has, f'不存在该状态: {value.name}'

    def set_initial_state(self, state):
        if not isinstance(state, State):
            raise ValueError('initial_state必须为State对象')
        self.initial_state = state

    def on_enter(self):
        return self.current_state.on_enter()

    def on_exit(self):
        self.current_state.reset_duration_condition()
        return self.current_state.on_exit()

    def _on_process(self, delta):
        return self.current_state.on_process(delta)

    def on_process(self):
        start_time = time.time()
        while True:
            if self.get_parameter('_stop_running'):
                logger.info("停止运行")
                break
            if self.get_parameter('_active'):
                # 休息1帧, 60分之1秒
                time.sleep(1 / 60)
                self.current_state.check_transition()
                end_time = time.time()
                delta = end_time - start_time
                start_time = end_time
                self._on_process(delta)
            else:
                time.sleep(1 / 60)

    def add_states(self, states):
        for state in states:
            self.add_state(state)

    def add_state(self, state):
        if not isinstance(state, State):
            raise ValueError('state必须为State对象')
        if state.name in [s.name for s in self.states]:
            return
        state.machine = self
        self.states.append(state)

    def get_state(self, name):
        for state in self.states:
            if state.name == name:
                return state
        return None

    def switch_state(self, name):
        state = self.get_state(name)
        if state is None:
            raise ValueError('不存在该状态')
        if self.current_state is not None:
            self.current_state.on_exit()
            self.current_state.is_active = False
        self.current_state = state
        self.current_state.is_active = True
        self.current_state.on_enter()

    def inject_set_parameter(self, key, value):
        manager = multiprocessing.Manager()
        namespace = manager.Namespace()
        global_parameters = self.global_parameters
        global_parameters[key] = value
        namespace.global_parameters = global_parameters
        self.global_parameters = namespace.global_parameters

    def set_parameter(self, key, value):
        self.global_parameters[key] = value

    def get_parameter(self, key):
        return self.global_parameters.get(key, None)

    def start(self):
        logger.info('开始运行')
        self.activate()
        self.set_parameter('_stop_running', False)
        if self.initial_state is None:
            raise ValueError('initial_state不能为空')
        if self.current_state is None:
            self.current_state = self.initial_state
        self.current_state.on_enter()
        self.t = threading.Thread(target=self.on_process)
        self.t.start()

    def stop(self):
        self.set_parameter('_stop_running', True)

    def build_from_json(self, json_data):
        if isinstance(json_data, str):
            json_data = json.loads(json_data)
        # name
        self.name = json_data.get('name', None)
        # states
        states = json_data.get('states', None)
        if states is not None:
            for state in states:
                self.add_state(self.build_state_from_json(state))

        # initial_state
        initial_state = json_data.get('initial_state', None)
        if initial_state is None and self.initial_state is None:
            raise ValueError('initial_state不能为空')
        self.initial_state = self.get_state(initial_state)

        # initial_parameters
        initial_parameters = json_data.get('initial_parameters', None)
        if initial_parameters is not None:
            for key, value in initial_parameters.items():
                self.set_parameter(key, value)

    def build_state_from_json(self, json_data):
        if isinstance(json_data, str):
            json_data = json.loads(json_data)
        name = json_data.get('name', None)
        if not name:
            raise ValueError('State name不能为空')
        _class = json_data.get('class', None)
        if not _class:
            state = State(name, machine=self)
        else:
            state = _class(name, machine=self)
        transitions = json_data.get('transitions', None)
        if transitions is not None:
            for transition in transitions:
                state.add_transition(self.build_transition_from_json(transition))

        return state

    def build_transition_from_json(self, json_data):
        if isinstance(json_data, str):
            json_data = json.loads(json_data)
        name = json_data.get('name', None)
        source = json_data.get('source', None)
        target = json_data.get('target', None)
        if not name:
            raise ValueError('Transition name不能为空')
        if not source:
            raise ValueError('Transition source不能为空')
        if not target:
            raise ValueError('Transition target不能为空')
        transition = Transition(name, source, target, machine=self)
        conditions = json_data.get('conditions', None)
        if conditions is not None:
            for condition in conditions:
                transition.add_condition(self.build_condition_from_json(condition))
        return transition

    def build_condition_from_json(self, json_data):
        if isinstance(json_data, str):
            json_data = json.loads(json_data)
        name = json_data.get('name', None)
        typ = json_data.get('type', None)
        if not name:
            raise ValueError('Condition name不能为空')
        if not typ:
            raise ValueError('Condition type不能为空')
        # guard
        if typ in ['all_guard', 'any_guard']:
            conditions = json_data.get('conditions', None)
            if not conditions:
                raise ValueError('all_guard conditions不能为空')
            conditions = [self.build_condition_from_json(condition) for condition in conditions]
            if typ == 'all_guard':
                condition = AllGuard(conditions)
                return condition
            elif typ == 'any_guard':
                condition = AnyGuard(conditions)
                return condition
        if typ == 'not_guard':
            _condition = json_data.get('condition', None)
            _condition = self.build_condition_from_json(_condition)
            condition = NotGuard(_condition)
            return condition
        # 正常condition
        value = json_data.get('value', None)
        if not value:
            raise ValueError('Condition value不能为空')
        condition = Condition(name, typ, value, machine=self)
        return condition
